﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Configuration;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
using System.Security.Cryptography;

namespace urlwatcher
{
    public class urlwatcher : IHttpModule
    {
        static string sRedirectTarget = "";
        static string sRedirectFilter = "";
        static string sAuthTarget = "";
        static string sAuthFilter = "";
        static string sAuthCheck = "";
        static string sAuthTokenName = "";
        static string sAuthParamName = "";
        static string sTargetUrlName = "";
        static long lAuthTime = 0;
        static byte[] secretKey = null;

        static ASCIIEncoding textConverter = new ASCIIEncoding();

        public static string loadSettingString(string defaultVal, string appSetting)
        {
            string res = defaultVal;
            try {res = WebConfigurationManager.AppSettings[appSetting];}
            catch(Exception e){}
            return res;
        }

        public static byte[] loadSettingBytes(byte[] defaultVal, string appSetting)
        {
            byte[] res = defaultVal;
            try 
            { 
                string sVal = WebConfigurationManager.AppSettings[appSetting];
                res = textConverter.GetBytes(sVal);
            }
            catch (Exception e) { }
            return res;
        }

        public static long loadSettingLong(long defaultVal, string appSetting)
        {
            long res = defaultVal;
            try
            {
                string sVal = WebConfigurationManager.AppSettings[appSetting];
                res = long.Parse(sVal);
            }
            catch (Exception e) { }
            return res;
        }

        public urlwatcher()
        {
            sAuthTarget = loadSettingString("","urlwatcher-authorization-target");
            sAuthFilter = loadSettingString("","urlwatcher-authorization-filter");
            sRedirectTarget = loadSettingString("","urlwatcher-redirector-target");
            sRedirectFilter = loadSettingString("","urlwatcher-redirector-filter");
            sAuthCheck = loadSettingString("","urlwatcher-authorization-check");
            sAuthTokenName = loadSettingString("","urlwatcher-authorization-token");
            sAuthParamName = loadSettingString("","urlwatcher-authorization-parameter");
            sTargetUrlName = loadSettingString("","urlwatcher-target-url-parameter");
            secretKey = loadSettingBytes(new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                "urlwatcher-shared-secret");
            lAuthTime = loadSettingLong(0,"urlwatcher-authorization-time");
        }

        public String ModuleName
        {
            get { return "urlwatcher"; }
        }

        public void Init(HttpApplication application)
        {
            application.BeginRequest +=
                (new EventHandler(this.Application_BeginRequest));
        }

        private string decrypt(byte[] key, string original)
        {
            byte[] encrypted = System.Convert.FromBase64String(original);
            byte[] fromEncrypted = TokenUtils.decryptECB(key, PaddingMode.ISO10126, encrypted);
            return textConverter.GetString(fromEncrypted);
        }

        private bool checkToken2(string sAuthUrl) {
            bool bResult = false;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sAuthUrl);
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader r = new StreamReader(response.GetResponseStream());
            string responseText = r.ReadToEnd();
            string decryptedToken = decrypt(secretKey, responseText);
            bResult = decryptedToken.Contains("approved");
            return bResult;
        }

        private bool checkToken(string text, string sAppId)
        {
            bool bResult = false;
            if (null != text)
            {
                string decryptedToken = decrypt(secretKey, text);
                string sTokenAppId = TokenUtils.parseValue(sAuthParamName, decryptedToken);
                string sDateTime = TokenUtils.parseValue("time", decryptedToken);
                if ("".Equals(sDateTime) || "".Equals(sTokenAppId)) { bResult = false; }
                else
                {
                    DateTime datetime = DateTime.Parse(sDateTime);
                    long timediff = (DateTime.Now.Ticks - datetime.Ticks) / 10000000;
                    if (timediff > lAuthTime) { bResult = false; }
                    else
                    {
                        if (!sTokenAppId.Equals(sAppId)) { bResult = false; }
                        else { bResult = true; }
                    }
                }
            }
            return bResult;
        }

        private bool testFilter(string filter, string text)
        {
            bool bResult = false;
            if (null != filter && !"".Equals(filter))
            {
                Regex regex = new Regex(filter, RegexOptions.IgnoreCase);
                bResult = regex.IsMatch(text);
            }
            return bResult;
        }

        private string cleanQueryString(string sParamName, 
            System.Collections.Specialized.NameValueCollection qureyString) 
        {
            string newQuery = "";
            foreach (string name in qureyString) 
            {
                if (!name.Equals(sParamName))
                {
                    if (0 < newQuery.Length) { newQuery += "&"; }
                        newQuery += name + "=" + qureyString[name];
                }
            }
            return newQuery;
        }

        private Uri makeUri(UriBuilder targetUri, string sUri) {
            Uri res = null;
            if (sUri.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
            {res = new Uri(sUri);}
            else
            {
                if (sUri.StartsWith("/", StringComparison.OrdinalIgnoreCase))
                {
                    res = (new UriBuilder(targetUri.Scheme, targetUri.Host, targetUri.Port, sUri)).Uri;
                }
                else 
                {
                    string[] segemnts = targetUri.Uri.Segments;
                    string newpath = "";
                    for (int cnt = 0; cnt < segemnts.Length - 1; ++cnt) { newpath += segemnts[cnt]; }
                    res = (new UriBuilder(targetUri.Scheme, targetUri.Host,
                        targetUri.Port, newpath + sUri)).Uri;
                }
            }
            return res;
        }

        private void watch(HttpContext ctx)
        {
            string sUrl = ctx.Request.Url.ToString();
            if (testFilter(sRedirectFilter, sUrl))
            {// todo:optional original URL packaging
                ctx.Response.Redirect(sRedirectTarget);
            }
            else
            {
                if (testFilter(sAuthFilter, sUrl))
                {// todo:optional original URL packaging
                    string authToken = ctx.Request.QueryString[sAuthTokenName];
                    string authParam = ctx.Request.QueryString[sAuthParamName];
                    if (null == authToken || "".Equals(authToken))
                    {//if the auth. token is not present we check just auth. filter
                        ctx.Response.Redirect(sAuthTarget + "?" +
                            sTargetUrlName +"=" + TokenUtils.encodeText(sUrl));
                    }
                    else
                    {
                        UriBuilder ub = new UriBuilder(ctx.Request.Url);
                        bool bApproved = false;
                        if ("".Equals(sAuthCheck)) { bApproved = checkToken(authToken, authParam); }
                        else 
                        {
                            Uri authCheckUri = makeUri(ub, sAuthCheck);
                            string sAuthUrl = authCheckUri.ToString() + "?" + sAuthParamName + "=" + authParam +
                                                            "&" + sAuthTokenName + "=" + HttpUtility.UrlEncode(authToken);
                            bApproved = checkToken2(sAuthUrl); 
                        }
                        if(!bApproved)
                        {
                            ub.Query = cleanQueryString(sAuthTokenName, ctx.Request.QueryString);
                            ctx.Response.Redirect(sAuthTarget + "?" +
                                sTargetUrlName + "=" + TokenUtils.encodeText(ub.Uri.ToString()));
                        }
                    }
                }
            }
        }

        private void Application_BeginRequest(Object source,
             EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            watch(context);
        }

        public void Dispose() { }
    }
}
